Skip to content

Add GitHub provider with env-driven auto-detection#4

Merged
lesnik512 merged 10 commits into
mainfrom
feat/github-provider
Jun 8, 2026
Merged

Add GitHub provider with env-driven auto-detection#4
lesnik512 merged 10 commits into
mainfrom
feat/github-provider

Conversation

@lesnik512

@lesnik512 lesnik512 commented Jun 8, 2026

Copy link
Copy Markdown
Member

Summary

Adds GitHubProvider as a peer of GitLabProvider. semvertag can now auto-tag GitHub repos in addition to GitLab. The package description's "Auto-tag GitLab repos" promise becomes honest: GitHub is the closing feature gap.

  • New GitHubProvider conforms to the existing Provider typing.Protocol — same four methods, GitHub-specific endpoints (/repos/{owner}/{repo}/{...}), tag creation via POST /git/refs, 422-already-exists translation
  • Settings.provider auto-detects from GITHUB_ACTIONS / GITLAB_CI env vars; explicit --provider github overrides; CI-ambiguous (both set) raises ConfigError
  • New Settings.repo: str field for OWNER/REPO; GitHubConfig.endpoint defaults to https://api.github.com (GitHub Enterprise users override)
  • New CLI flags: --provider, --repo, --github-endpoint. --token routes to the active provider via two-pass apply_cli_overlay
  • ioc.py gains github_client, github_provider, current_provider factories — the last mirrors the existing current_strategy selector pattern
  • _link_pagination extracted as a shared module (both providers walk RFC 8288 Link headers identically); _translate_transport extracted as a shared helper parameterized on provider_label
  • New docs/providers/github.md with inline GHA workflow recipe, token-scope guidance, GitHub Enterprise setup, troubleshooting
  • README hero updated; new "Use it in GitHub Actions" section
  • pyproject.toml description + keywords reflect dual-provider support

Design spec: planning/specs/2026-06-08-github-provider-design.md
Implementation plan: planning/plans/2026-06-08-github-provider.md

Test plan

  • just lint-ci — clean (ruff format + ruff check + ty)
  • just test — 417 passed, 100% statement+branch coverage
  • mkdocs build --strict — clean
  • DI smoke test: GITHUB_ACTIONS=true + GITHUB_REPOSITORY=octocat/repo resolves ProvidersGroup.current_provider to GitHubProvider
  • DI back-compat smoke test: CI_PROJECT_ID=999 (no GH env) resolves ProvidersGroup.current_provider to GitLabProvider
  • CI matrix runs across Python 3.11–3.14
  • Optional: end-to-end smoke against a real github.com test repo before merging

Out of scope (follow-ups)

  • action.yml (GitHub Actions composite-action wrapper) — the inline-job recipe in docs/README is the supported path until the composite is published
  • Bitbucket provider — same pattern would apply, separate PR

🤖 Generated with Claude Code

lesnik512 and others added 10 commits June 8, 2026 14:31
Add GitHubProvider parallel to GitLabProvider, conforming to the
existing Provider Protocol. Adds Settings.provider auto-detection
(GITHUB_ACTIONS / GITLAB_CI env), Settings.repo for owner/repo,
GitHubConfig.endpoint for Enterprise. New providers/github.py,
translate_github + translate_create_tag_github_unprocessable in
providers/_errors.py, current_provider selector in ioc.py mirroring
the current_strategy idiom. Extracts pagination helpers to
_link_pagination.py and shared _translate_transport helper at the
provider-label boundary. action.yml deferred to a follow-up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Seven-task plan derived from the 2026-06-08-github-provider design
spec. Order: extract _link_pagination (pure refactor), extract
_translate_transport (pure refactor), Settings.provider + validator
(TDD), translate_github + 422-already-exists (TDD), GitHubProvider +
integration tests (port-style), wire ioc.py + CLI + docs + README +
pyproject, final validation. Resolves all five spec open items inline.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…vider_label)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…dator

- Add `provider` field (gitlab|github) with AliasChoices for SEMVERTAG_PROVIDER/PROVIDER
- Add `repo` field with AliasChoices for SEMVERTAG_REPO/GITHUB_REPOSITORY
- Add `GitHubConfig.endpoint` defaulting to "https://api.github.com"
- Add `_detect_provider_from_env()` that auto-detects from GITHUB_ACTIONS/GITLAB_CI env vars, raising ValueError on ambiguous dual-CI context
- Add `_resolve_provider` model_validator (mode="after") that auto-detects provider, then enforces github→repo and gitlab→project_id constraints
- Add `populate_by_name=True` to Settings.model_config so field names can be used in init kwargs and apply_cli_overlay
- Remove dead `project_id is None` guard in ioc._build_gitlab_provider (now enforced by validator); drop unused ConfigError import
- Cascade-fix existing tests: add project_id=_PROJECT_ID_INT_SEMVERTAG to Settings() calls, update --project-id integration test to use mock-served project id 999, update project_id_missing assertion to match new validator message, add ioc test for _build_gitlab_client coverage

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…_unprocessable

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ioc.py: add github_client, github_provider, current_provider factories
  mirroring the gitlab pattern; SemvertagUseCase now consumes
  current_provider which dispatches on settings.provider
- __main__.py: add --provider, --repo, --github-endpoint flags;
  --token routes to the active provider via two-pass apply_cli_overlay;
  MAIN_APP help string refreshed to "GitLab and GitHub repos"
- tests/unit/test_ioc.py: add resolution tests for both provider paths
- tests/integration/test_cli_main_verb.py: add --provider github smoke
  tests + GITHUB_ACTIONS auto-detection round-trip
- tests/integration/test_cli_errors.py: minimal fix for cascading
  settings validator
- docs/providers/github.md: new — auth, env vars, inline GHA workflow
  recipe, troubleshooting (parallel to docs/providers/gitlab.md)
- mkdocs.yml: add GitHub Actions to nav
- README.md: hero updated to "GitLab or GitHub"; new "Use it in
  GitHub Actions" section with inline workflow
- pyproject.toml: description + keywords reflect dual-provider support

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
CI runs on GitHub Actions, which auto-sets GITHUB_ACTIONS=true. The new
Settings._resolve_provider validator picks that up and routes to the
github provider — breaking every integration test that mocks GitLab
endpoints (they 401 because the github auth header isn't on the
GitLab-shaped mock). Tests passed locally because neither env was set.

Fix: an autouse fixture in tests/conftest.py monkeypatch.delenv's both
CI markers + SEMVERTAG_PROVIDER + PROVIDER before every test runs.
Tests that exercise auto-detection still set them explicitly.

Verified locally with both GITHUB_ACTIONS=true and GITLAB_CI=true
prefixed to just test — 417 pass under both.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…er markers

Previous fix only stripped GITHUB_ACTIONS / GITLAB_CI / SEMVERTAG_PROVIDER /
PROVIDER. But GitHub Actions also auto-exports GITHUB_REPOSITORY (aliased into
Settings.repo) and GITHUB_TOKEN (aliased into Settings.github.token), so
test_provider_github_requires_repo silently failed in CI because repo was
populated by the runner's env.

Extract the full alias list as _HOST_CI_ENV_VARS and strip everything:
provider markers, repo/project identifiers, token aliases, endpoint overrides.

Verified locally with GITHUB_ACTIONS=true GITHUB_REPOSITORY=owner/repo
GITHUB_TOKEN=ghp_xxx CI_PROJECT_ID=999 CI_JOB_TOKEN=glpat-xxx just test —
417 pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@lesnik512 lesnik512 self-assigned this Jun 8, 2026
@lesnik512 lesnik512 merged commit 925bf99 into main Jun 8, 2026
5 checks passed
@lesnik512 lesnik512 deleted the feat/github-provider branch June 8, 2026 17:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant